package com.example.demo.zookeeper.mytest.lock;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.LockSupport;

/**
 * @author liweizhi
 * @date 2020/6/24
 */
public class LockWatcherCallback implements AsyncCallback.StringCallback, AsyncCallback.ChildrenCallback, Watcher, AsyncCallback.StatCallback {
    private ZooKeeper zooKeeper;
    private String threadName;
    private String realName;
    private CountDownLatch cdl = new CountDownLatch(1);

    public String getThreadName() {
        return threadName;
    }

    public void setThreadName(String threadName) {
        this.threadName = threadName;
    }

    public ZooKeeper getZooKeeper() {
        return zooKeeper;
    }

    public void setZooKeeper(ZooKeeper zooKeeper) {
        this.zooKeeper = zooKeeper;
    }

    public void lock() {
        // todo 这里需要先get一下"/",看其值是否是当前线程名,以实现可重入
        zooKeeper.create(ZkLockUtil.theLockPath, threadName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, this, "qwq");
        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void unLock() {
        try {
            zooKeeper.delete(realName, -1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    // create-stringCallBack
    @Override
    public void processResult(int rc, String path, Object ctx, String name) {
        if (name != null) {
            System.out.println(threadName + " create : " + name);
            realName = name;
            zooKeeper.getChildren("/", false, this, "qwq");
        }
    }

    // getChildren-childrenCallback
    @Override
    public void processResult(int rc, String path, Object ctx, List<String> children) {
        Collections.sort(children);
        int index = children.indexOf(realName.substring(1));
        if (index == 0) {
            // 轮到自己了
            System.out.println(threadName + " getted lock");
            // 这里set一下,一是防止业务逻辑执行的太快,导致下一个节点还没监听上呢,就删除了;导致下一个节点永远不能监听到"上一个节点"的删除事件
            // 第二是为了支持锁的"可重入", lock的时候就需要先get一下,看是否是当前线程
            try {
                zooKeeper.setData("/", threadName.getBytes(), -1);
                cdl.countDown();
            } catch (KeeperException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        } else {
            // 监听上一位
            zooKeeper.exists("/" + children.get(index - 1), this, this, "qwq");
        }
    }

    // exists-watcher
    @Override
    public void process(WatchedEvent event) {
        switch (event.getType()) {
            case None:
                break;
            case NodeCreated:
                break;
            case NodeDeleted:
                // 上一个节点删除了,该我了
                zooKeeper.getChildren("/", false, this, "qwq");
                break;
            case NodeDataChanged:
                break;
            case NodeChildrenChanged:
                break;
            case DataWatchRemoved:
                break;
            case ChildWatchRemoved:
                break;
            case PersistentWatchRemoved:
                break;
            default:
                break;
        }
    }

    // exists-StatCallback
    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        // todo 如果判定上一个节点exists时,刚好它不存在了(客户端挂了,或者释放了),这时候就监控失败 watcher不好使了
    }
}
